import { cookies } from 'next/headers';
import { isPlainObject, tryParse } from '../common';
import { Logger } from './logger';

const logger = Logger('fetch');

async function bfetch<T = Obj<any>>(
  url: string,
  options: RequestInit = {},
  method: 'GET' | 'POST' = 'GET'
): Promise<APIRes<T>> {
  if (!url.startsWith('http')) {
    if (!/^\//.test(url)) {
      url = '/' + url;
    }
    url = process.env.NEXT_PUBLIC_DSZ_API_DOMAIN + url;
  }
  const cookiesObj = cookies();
  const token = cookiesObj.get('token')?.value || 'none';
  const cache = 'force-cache' as RequestCache;
  // const cache = 'no-cache' as RequestCache;
  const headers = {
    'Content-Type': 'application/json',
    ...(token != 'none' && { Authorization: `jwt ${token}` }),
  };
  options.headers = { ...headers, ...options.headers };
  try {
    const reqOptions = { method, ...options };

    if (process.env.NEXT_PUBLIC_APP_ENV !== 'production') {
      logger.warn(
        `FETCHING ${url} WITH OPTIONS: ${JSON.stringify(reqOptions)}`
      );
    }

    const res = await fetch(url, {
      // cache, // cache and revalidate can not be used together, only one will take effect
      next: { revalidate: 10 * 60 },
      ...reqOptions,
    });
    const contentType = res.headers.get('Content-Type') || '';
    if (res.ok) {
      const result = /json/.test(contentType)
        ? await res.json()
        : /form/.test(contentType)
          ? await res.formData()
          : await res.text();
      return { data: result, message: 'ok', success: true };
    } else {
      const text = await res.text();
      const parsed = tryParse(text);
      const data = isPlainObject(parsed) ? parsed : { message: text };
      // datadog will treat each line as a separate log, so we need to join them together
      // when api-gateway return 502, text is html
      const oneLineText = text.replace(/\n|\r/g, '|NEWLINE|');
      logger.error(
        `${url} ${res.statusText}  (${res.status}) body-->:  (${oneLineText})`
      );
      return {
        data: { data, status: res.status } as any,
        message: `${data.code || ''} ${data.message || ''}`,
        success: false,
      };
    }
  } catch (err: any) {
    const oneLineErr = (err.message || '').replace(/\n|\r/g, '|NEWLINE|');
    logger.error('BetterFetch error:', oneLineErr, url);
    return { data: err, message: `API Fail: ${err.message}`, success: false };
  }
}

async function pfetch<T = Obj>(
  url: string,
  options: RequestInit = {}
): Promise<APIRes<T>> {
  return await bfetch(url, options, 'POST');
}

async function gfetch<T = Obj>(
  url: string,
  options: RequestInit = {}
): Promise<APIRes<T>> {
  return await bfetch(url, options, 'GET');
}

export { gfetch, pfetch };
